/**
 * Copyright 2000 self.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 *
 * @author Ashin Wimalajeewa
 */
package self.amigo.tool.sphinx.ui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.swing.Action;
import javax.swing.JDialog;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.event.MenuEvent;

import self.amigo.module.CloseInfo;
import self.amigo.module.IEditingEnvironment;
import self.amigo.module.IEditingHost;
import self.amigo.module.IModule;
import self.amigo.module.ModuleManagerUtils;
import self.amigo.module.OpenInfo;
import self.amigo.module.SaveInfo;
import self.amigo.tool.sphinx.Editor;
import self.gee.editor.IllegalUndoException;
import self.lang.StringUtils;

public class EditorHost implements IEditingHost {
  final public EditorWindow    window;
  private ArrayList             modules = new ArrayList();
  private ArrayList             viewsToClose = new ArrayList();
  private ArrayList             menusToInstall = new ArrayList();
  private ArrayList             environments = new ArrayList();
  private IEditingEnvironment   active;
  private Map                   properties = new HashMap();

  public EditorHost( EditorWindow wnd, Map props ) {
    window = wnd;
    properties.put( Editor.GLOBAL_PROP, props );
    initProperties();
    initModules();
    initEnvironments();
    initListeners();
    constructNewMenuFromEnvironments( window.fileNewItem );
  }

  // IModuleHost support
  public Object getValue( Object name ) {
    return properties.get( name );
  }

  public void   setValue( Object name, Object val ) {
    properties.put( name, val );
  }

  public void unmount() {
    if ( active != null ) {
      Component cmp = active.toComponent();
      cmp.setVisible( false );
      window.remove( active.toComponent() );
      active.setActive( false );
      fileItem_focusGained( null );
    }
    setTitle( null );
    active = null;
  }

  public void mount( IEditingEnvironment env ) {
    if ( env != active ) {
      unmount();
      if ( env != null ) {
        Component cmp = env.toComponent();
        window.getContentPane().add( cmp, BorderLayout.CENTER );
        cmp.setVisible( false );
        cmp.setVisible( true );
        menusToInstall.clear();
        env.getInstallMenus( menusToInstall );
        window.resetMenu( menusToInstall );
        env.setActive( true );
      }
      active = env;
      fileItem_focusGained( null );
    }
  }

  public void reportStatus( String msg ) {
    if ( StringUtils.isNullOrEmpty(msg) )
      window.statusMsg.setText( EditorWindow.EMPTY_MSG );
    else
      window.statusMsg.setText( msg );
  }

  public void promptMessage( String title, String msg ) {
    window.promptInfoMessage( title, msg );
  }

  public int  promptForChoice( String title, String msg, int options ) {
    return window.promptForChoice( title, msg, options );
  }

  public void setTitle( String title ) {
    window.setTitleCaption( title );
  }

  private void initModules() {
    ModuleManagerUtils.getModules( modules );
  }

  private void initEnvironments() {
    int max = modules.size();
    for ( int cntr = 0; cntr < max; cntr++ ) {
      IModule mod = (IModule) modules.get( cntr );
      IEditingEnvironment env = mod.createEditingEnvironment();
      env.setHost( this );
      environments.add( env );
    }
  }

  private void initProperties() {
    properties.put( FILE_NEW_MI_KEY         , window.fileNewItem );
    properties.put( FILE_OPEN_MI_KEY        , window.fileOpenItem );
    properties.put( FILE_OPEN_FROM_CB_MI_KEY, window.fileOpenFromClipboardItem );
    properties.put( FILE_CLOSE_MI_KEY       , window.fileCloseItem );
    properties.put( FILE_CLOSE_ALL_MI_KEY   , window.fileCloseAllItem );
    properties.put( FILE_SAVE_MI_KEY        , window.fileSaveItem );
    properties.put( FILE_SAVE_AS_MI_KEY     , window.fileSaveAsItem );
    properties.put( FILE_SAVE_TO_CB_MI_KEY  , window.fileSaveToClipboardItem );
    properties.put( FILE_EXIT_MI_KEY        , window.fileExitItem );
    properties.put( EDIT_UNDO_MI_KEY        , window.editUndoItem );
    properties.put( EDIT_REDO_MI_KEY        , window.editRedoItem );
    properties.put( EDIT_CUT_MI_KEY         , window.editCutItem );
    properties.put( EDIT_COPY_MI_KEY        , window.editCopyItem );
    properties.put( EDIT_PASTE_MI_KEY       , window.editPasteItem );
    properties.put( EDIT_DELETE_MI_KEY      , window.editDeleteItem );
    properties.put( EDIT_SELECT_ALL_MI_KEY  , window.editSelectAllItem );
  }

  private void constructNewMenuFromEnvironments( JMenu dropDown ) {
    ArrayList store = new ArrayList();
    int max = environments.size();
    for ( int cntr = 0, mnu = -1; cntr < max; cntr++ ) {
      store.clear();
      IEditingEnvironment env = (IEditingEnvironment) environments.get( cntr );
      env.getNewCommandActions( store );
      int items = store.size();
      if ( (cntr > 0) && (items > 0) )
        dropDown.addSeparator();
      for ( int itm = 0; itm < items; itm++ ) {
        Action act = (Action) store.get(itm);
        if ( act == null )
          dropDown.addSeparator();
        else {
          JMenuItem menuItem = dropDown.add( act );
          String curr = (String) act.getValue( Action.NAME );
          menuItem.setText( "" + ++mnu + " " + curr );
          if ( mnu < 10 ) {
            String sMnemo = "" + mnu;
            menuItem.setMnemonic( sMnemo.charAt(0) );
          }
        }
      }
    }
  }

  private void constructWindowsOpenMenuFromModules( JMenu dropDown ) {
    ArrayList store = new ArrayList();
    int max = environments.size();
//    ButtonGroup grp = new ButtonGroup();
    for ( int cntr = 0, wnd = 0; cntr < max; cntr++ ) {
      store.clear();
      IEditingEnvironment env = (IEditingEnvironment) environments.get( cntr );
      env.getWindowOpenCommandActions( store );
      int items = store.size();
      for ( int itm = 0; itm < items; itm++, wnd++ ) {
        if ( itm == 0 )
          dropDown.addSeparator();
        Action act = (Action) store.get(itm);
        String curr = (String) act.getValue( Action.NAME );
        JMenuItem menu = dropDown.add( act );
        menu.setText( "" + wnd + " " + curr );
        if ( wnd < 10 ) {
          String sMnemo = "" + wnd;
          menu.setMnemonic( sMnemo.charAt(0) );
        }
      }
    }
  }

  private IEditingEnvironment getEnvironmentForSource( OpenInfo info ) throws IOException {
    int max = environments.size();
    if ( max == 0 )
      return null;
    Reader rdr = info.getReader();
    rdr.mark( 1000000 );
    IEditingEnvironment ret = null;
    try {
      for ( int cntr = 0; cntr < max; cntr++ ) {
        if ( cntr > 0 )
          rdr.reset();
        IEditingEnvironment env = (IEditingEnvironment) environments.get( cntr );
        if ( env.getModule().getTranslator().canHandle(rdr) ) {
          ret = env;
          break;
        }
      }
    } finally {
      rdr.close();
    }
    return ret;
  }

  private boolean closeView( IEditingEnvironment env, CloseInfo close ) {
    boolean cont = true;
    if ( close.isModified ) {
      String msg = close.title;
      msg = "Do you wish to save [" + msg + "]";
      int option = promptForChoice( "Save modified source", msg, JOptionPane.YES_NO_CANCEL_OPTION );
      if ( option == JOptionPane.CANCEL_OPTION )
        cont = false;
      else {
        if ( option == JOptionPane.NO_OPTION )
          env.close( close.view );
        else {
          if ( close.view.type == OpenInfo.FILE_SYSTEM )
            saveViewWithExistingName( close.view );
          else {
            cont = saveViewWithPromptedName( close.view );
          }
        }
      }
    } else
      env.close( close.view );
    return cont;
  }

  private boolean closeAllViews() {
    int envCount = environments.size();
    for ( int ecntr = 0; ecntr < envCount; ecntr++) {
      IEditingEnvironment env = (IEditingEnvironment) environments.get( ecntr );
      viewsToClose.clear();
      env.getCloseInfos( viewsToClose, false );
      int max = viewsToClose.size();
      for ( int cntr = 0; cntr < max; cntr++ ) {
        CloseInfo close = (CloseInfo) viewsToClose.get( cntr );
        if ( !closeView(env, close) )
          return false;
      }
    }
    return true;
  }

  private void saveViewWithExistingName( OpenInfo open ) {
    SaveInfo save = new SaveInfo( SaveInfo.EXISTING_NAME, null );
    try {
      active.save( open, save );
    } catch ( Exception err ) {
      err.printStackTrace();
      throw new IllegalStateException( err.getMessage() );
    }
  }

  private boolean saveViewWithPromptedName( OpenInfo open ) {
    File file = window.saveDialog();
    if ( file != null ) {
      SaveInfo save = new SaveInfo( SaveInfo.FILE_SYSTEM, file );
      try {
        active.save( open, save );
        return true;
      } catch ( Exception err ) {
        err.printStackTrace();
        throw new IllegalStateException( err.getMessage() );
      }
    }
    return false;
  }

  private CloseInfo getActiveView() {
    viewsToClose.clear();
    active.getCloseInfos( viewsToClose, true );
    return (CloseInfo) viewsToClose.get( 0 );
  }

  // Windowing Events
  void window_windowClosing(WindowEvent e) {
    fileExitItem_actionPerformed( null );
  }

  private void window_focusGained(FocusEvent e) {
    Editor.instance.activeHost = this;
  }

  private void fileItem_focusGained(FocusEvent e) {
    boolean enable = (active != null);
    window.fileCloseItem.setEnabled( enable );
    window.fileCloseAllItem.setEnabled( enable );
  }

//  private void windowItem_focusGained(FocusEvent e) {
    private void windowItem_menuSelected( MenuEvent e ) {
    window.windowItem.removeAll();
    window.windowItem.add( window.windowNewEditorItem );
    constructWindowsOpenMenuFromModules( window.windowItem );
  }

//  private void editItem_focusGained(FocusEvent e) {
//    window.editItem.setEnabled( ClipboardUtils.isContentAvailable() );
//  }

  private void fileOpenItem_actionPerformed(ActionEvent e) {
    File file = window.openDialog();
    if ( file != null ) {
      OpenInfo info = new OpenInfo( OpenInfo.FILE_SYSTEM, file );
      try {
        IEditingEnvironment env = getEnvironmentForSource( info );
        env.open( info );
      } catch ( Exception err ) {
        err.printStackTrace();
        window.promptErrorMessage( "Error", "Invalid File format to read from" );
//        throw new IllegalStateException( err.getMessage() );
      }
    }
  }

  private void fileOpenFromClipboardItem_actionPerformed(ActionEvent e) {
    OpenInfo info = new OpenInfo( OpenInfo.CLIPBOARD, null );
    try {
      IEditingEnvironment env = getEnvironmentForSource( info );
      env.open( info );
    } catch ( Exception err ) {
//      err.printStackTrace();
      window.promptErrorMessage( "Error", "Invalid Clipboard format to read from" );
//      throw new IllegalStateException( err.getMessage() );
    }
  }

  private void fileCloseItem_actionPerformed(ActionEvent e) {
    closeView( active, getActiveView() );
  }

  private void fileCloseAllItem_actionPerformed(ActionEvent e) {
    closeAllViews();
  }

  private void fileSaveItem_actionPerformed(ActionEvent e) {
    saveViewWithExistingName( getActiveView().view );
  }

  private void fileSaveAsItem_actionPerformed(ActionEvent e) {
    saveViewWithPromptedName( getActiveView().view );
  }

  private void fileSaveToClipboardItem_actionPerformed(ActionEvent e) {
    SaveInfo info = new SaveInfo( SaveInfo.CLIPBOARD, null );
    try {
      active.save( getActiveView().view, info );
    } catch ( Exception err ) {
      err.printStackTrace();
      throw new IllegalStateException( err.getMessage() );
    }
  }

  private void fileExitItem_actionPerformed(ActionEvent e) {
    if ( active != null )
      if ( !closeAllViews() )
        return;
    window.setVisible( false );
    window.dispose();
    Editor.instance.exit( this );
  }

  private void editUndoItem_actionPerformed(ActionEvent e) {
    try {
      active.undo();
    } catch ( IllegalUndoException err ) {
      reportStatus( err.getMessage() );
    }
  }

  private void editRedoItem_actionPerformed(ActionEvent e) {
    active.redo();
  }

  private void editCutItem_actionPerformed(ActionEvent e) {
    try {
      active.cut();
    } catch ( Exception err ) {
      window.promptErrorMessage( "Error", "Unable to cut selected elements to clipboard" );
    }
  }

  private void editCopyItem_actionPerformed(ActionEvent e) {
    try {
      active.copy();
    } catch ( Exception err ) {
      window.promptErrorMessage( "Error", "Unable to copy selected elements to clipboard" );
    }
  }

  private void editPasteItem_actionPerformed(ActionEvent e) {
    if ( active == null )
      return;
    try {
      active.paste();
    } catch ( Exception err ) {
      window.promptErrorMessage( "Error", "Unable to paste from clipboard" );
    }
  }

  private void editDeleteItem_actionPerformed(ActionEvent e) {
    if ( active == null )
      return;
    active.delete();
  }

  private void editSelectAllItem_actionPerformed(ActionEvent e) {
    if ( active == null )
      return;
    active.selectAll();
  }

  private void windowNewEditorItem_actionPerformed(ActionEvent e) {
    // Diag layers must have there own painters before doing this
  }

  private void initListeners() {
    window.helpAboutItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        Splash splash = new Splash( modules );
        JOptionPane pane = new JOptionPane( splash.getContentPane(), JOptionPane.PLAIN_MESSAGE );
        JDialog dlg = pane.createDialog( null, "About" );
        dlg.setResizable( false );
        dlg.show();
      }
    });


    window.addWindowListener(new java.awt.event.WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        window_windowClosing(e);
      }
    });

//    window.editItem.addFocusListener(new java.awt.event.FocusAdapter() {
//      public void focusGained(FocusEvent e) {
//        editItem_focusGained(e);
//      }
//    });

    window.fileItem.addFocusListener(new java.awt.event.FocusAdapter() {
      public void focusGained(FocusEvent e) {
        fileItem_focusGained(e);
      }
    });

//    window.windowItem.addFocusListener(new java.awt.event.FocusAdapter() {
//      public void focusGained(FocusEvent e) {
//        windowItem_focusGained(e);
//      }
//    });

    window.windowItem.addMenuListener(new javax.swing.event.MenuListener() {
      public void menuSelected(MenuEvent e) {
        windowItem_menuSelected(e);
      }
      public void menuDeselected(MenuEvent e) {
    	// nothing to do.
      }
      public void menuCanceled(MenuEvent e) {
    	// nothing to do.
      }
    });

    window.fileCloseItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fileCloseItem_actionPerformed(e);
      }
    });

    window.fileCloseAllItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fileCloseAllItem_actionPerformed(e);
      }
    });

    window.addFocusListener(new java.awt.event.FocusAdapter() {
      public void focusGained(FocusEvent e) {
        window_focusGained(e);
      }
    });

    window.fileExitItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fileExitItem_actionPerformed(e);
      }
    });

    window.fileOpenItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fileOpenItem_actionPerformed(e);
      }
    });

    window.fileOpenFromClipboardItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fileOpenFromClipboardItem_actionPerformed(e);
      }
    });

    window.windowNewEditorItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        windowNewEditorItem_actionPerformed(e);
      }
    });

    window.editUndoItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        editUndoItem_actionPerformed(e);
      }
    });

    window.editRedoItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        editRedoItem_actionPerformed(e);
      }
    });

    window.editCutItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        editCutItem_actionPerformed(e);
      }
    });

    window.editCopyItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        editCopyItem_actionPerformed(e);
      }
    });

    window.editPasteItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        editPasteItem_actionPerformed(e);
      }
    });

    window.editDeleteItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        editDeleteItem_actionPerformed(e);
      }
    });

    window.editSelectAllItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        editSelectAllItem_actionPerformed(e);
      }
    });

    window.fileSaveItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fileSaveItem_actionPerformed(e);
      }
    });

    window.fileSaveAsItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fileSaveAsItem_actionPerformed(e);
      }
    });

    window.fileSaveToClipboardItem.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        fileSaveToClipboardItem_actionPerformed(e);
      }
    });
  }
}
